/*
 * Copyright 2009, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

//
// Parse HLSL shaders and separate the shader code from the Technique block
// parse the technique block into shader declarations and stateassignments
// using a Parser and Lexer generated by Antlr3.0
//
// Compile from the command line using:
//
//   $ set CLASSPATH=%CLASSPATH%;c:\bin\antlr\antlr-3.1.jar
//   $ java org.antlr.Tool Technique.g3pl
//   $ cl *.cc *.c /TP -I c:\bin\antlr\libantlr3c-3.1\include /c /nologo
//   $ link *.obj /LTCG /out:technique.exe c:\bin\antlr\antlr3c.lib \
//                /NODEFAULTLIB:libcmt.lib /nologo


grammar Technique;

options {
   language = C;
}

@lexer::preincludes {
#include <vector>

#ifdef OS_WIN
// These need to be defined here so that when antlr includes
// winsock.h, it doesn't barf.  For some reason these are undefined by
// some header (and it appears to be a windows header).
#define IN
#define OUT
#endif
}

@lexer::postinclude {
  namespace RB {
  void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer,
                      pANTLR3_UINT8 * tokenNames);
  }
}

@lexer::apifuncs
{
  // Install the custom error reporting function.
  RECOGNIZER->displayRecognitionError = RB::TechniqueError;
}

@lexer::members {
  static int toInt(const pANTLR3_STRING string) {
    return atoi(reinterpret_cast<const char*>(string->chars));
  }
  static std::string toStringL(const pANTLR3_STRING string) {
    return std::string(reinterpret_cast<const char*>(string->chars));
  }
  // Removes escape sequences, as well as leading and trailing quotes.
  static ANTLR3_STRING* unescape(const pANTLR3_STRING string) {
    if (!string || !string->factory) return NULL;
    ANTLR3_STRING* s = string->factory->newRaw(string->factory);
    const char *f = reinterpret_cast<const char*>(string->chars);
    if (!f) return s;
    if (*f == '"') f++;
    for (; *f; f++) {
      if (*f == '\\') {
        switch (*++f) {
          case '\0':  return s;
          case 'b':   s->addc(s, '\b'); break;
          case 't':   s->addc(s, '\t'); break;
          case 'n':   s->addc(s, '\n'); break;
          case 'f':   s->addc(s, '\f'); break;
          case 'r':   s->addc(s, '\r'); break;
          default:    s->addc(s, *f);   break;
        }
      } else if (*f == '"') {
        return s;
      } else {
        s->addc(s, *f);
      }
    }
    return s;
  }
  static void setFilename(pANTLR3_BASE_RECOGNIZER rec,
                          const pANTLR3_STRING filename) {
    if (filename) {
      // We store the filename into the "custom" field in the lexer state,
      // which gets copied into each token.  If we use the fileName field
      // in the stream itself, when the lexer runs ahead of the parser
      // (which is often), the file name can be incorrect.  By storing it
      // in the token itself, the error code can retrieve the filename
      // associated with that token.
      rec->state->custom = unescape(filename);
    }
  }
}

@parser::preincludes {
// NOTE: this header must be included before the "antlr.h" header
// because on Windows it includes <windows.h> which will define <winsock.h>
// before <winsock2.h>, causing a cascade of struct redefinition errors.
// NOTE: disable compiler warning about C function returning a
// struct. This is caused by the Antlr generated functions being declared
// extern "C" but being compiled and used as C++.
#pragma warning( disable: 4190 )
#include <vector>
#include "EffectDescriptorTypes.h"
using namespace FXParser;
}

@parser::includes {
// NOTE: includes that occur after the "antlr3.h" header has been
// declared which therefore can use the Antlr3 datatypes.
}

@parser::postinclude {
  namespace RB {
  void TechniqueError(pANTLR3_BASE_RECOGNIZER recognizer,
                      pANTLR3_UINT8 * tokenNames);
  void TechniqueSetErrorString(std::string* e);
  }
}

@parser::apifuncs
{
  // Install the custom error reporting function.
  RECOGNIZER->displayRecognitionError = RB::TechniqueError;
}

@parser::members {
  std::string *shader_string_;
  FXDesc *fxDesc_;
  std::string *error_string_;
  std::string toString(const pANTLR3_STRING string) {
    return std::string((string != NULL) ? reinterpret_cast<const char*>(string->chars) : "");
  }
  void addString(const pANTLR3_STRING string) {
    shader_string_->append(toString(string));
  }
  void addText(const pANTLR3_STRING string) {
    if (string && string->chars) {
      addString(string);
      shader_string_->append("\n");
    }
  }
}

// rules -----------------------------------------------------------------------

// This is the entry rule - zero or more global declarations followed by an
// end-of-file token.
translation_unit [FXDesc *fxDesc,
                  std::string *shader_string,
                  std::string *error_string]
  returns [bool success]
@init {
  // On entry, reset the list of error strings.
  fxDesc_ = fxDesc;
  shader_string_ = shader_string;
  error_string_ = error_string;
  RB::TechniqueSetErrorString(error_string_);
}
@after {
  // On exit, set the return value.
  success = error_string_->length() == 0;
  fxDesc_ = NULL;
  shader_string_ = NULL;
  error_string_ = NULL;
  RB::TechniqueSetErrorString(NULL);
}
  :
    ( noise global_declaration )* EOF
  ;

noise
  :
    ( COMMENT | WHITESPACE | MULTILINE_COMMENT )* { addText($text); }
    | LINE_DIRECTIVE
  ;

global_declaration
  : function_declaration
    { addText($text); }
  | struct_definition
    { addText($text); }
  | var_declaration
    { addText($text); }
  | typedef_definition
    { addText($text); }
  | technique_definition
  ;

// variables -------------------------------------------

var_declaration
@declarations {
  UniformDesc uniform_decl;
}
@init {
}
@after {
  fxDesc_->Uniforms.push_back(uniform_decl);
}

  : var_storage_class*
    var_type_modifier?
    var_datatype id_declaration
    {
        uniform_decl.Name = toString($id_declaration.text);
        uniform_decl.Type = toString($var_datatype.text);
    }
    semantic?
    {
        uniform_decl.Semantic = toString($semantic.specifier);
    }
    (annotation_list[uniform_decl.Annotations]) ?
    ('=' initializer)?
    {
    	uniform_decl.Value = $initializer.valueInit;
    }
    var_packoffset?
    var_register_bind?
    SEMI
  ;

var_storage_class
  : EXTERN
  | NOINTERPOLATION
  | SHARED
  | STATIC
  | UNIFORM
  | VOLATILE
  ;

var_type_modifier
  : T_CONST
  | ROW_MAJOR
  | COLUMN_MAJOR;

var_datatype
  : buffer_type_specifier
  | scalar_type_or_string_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  | sampler_type_specifier
  | texture_type_specifier
  ;

var_packoffset
  : 'packoffset' LPAREN register_name (DOT IDENTIFIER)? RPAREN
  ;

var_register_bind
  : COLON register_name
  ;

id_declaration
  : IDENTIFIER ( LBRACKET expression RBRACKET )?
  ;

// function --------------------------------------------

function_declaration
  : function_storage_class?
    function_type_specifier IDENTIFIER LPAREN argument_list RPAREN semantic?
    function_body (SEMI)?
  ;

function_storage_class
  : INLINE   // ignoring platform target
  ;

function_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  | T_VOID
  ;

semantic returns [pANTLR3_STRING specifier]
  :    COLON semantic_specifier 
  {
  	specifier = $semantic_specifier.text;
  }
  ;

param_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | struct_type_specifier
  | string_type_specifier
  | sampler_type_specifier
  ;

basic_type_specifier
  : scalar_type_specifier
  | vector_type_specifier
  | matrix_type_specifier
  | string_type_specifier
  ;

// typedef ---------------------------------------

typedef_definition
  : TYPEDEF
  ;

// basic datatypes -------------------------------

buffer_type_specifier
  : (BUFFER '<' var_datatype '>' IDENTIFIER)
  ;

// effects ---------------------------------------------------------------------

technique_definition
@declarations {
  TechniqueDesc technique_decl;
}
@init {
}
  : TECHNIQUE IDENTIFIER
    {
      technique_decl.Name = toString($IDENTIFIER.text);
    }
    (annotation_list[technique_decl.Annotations]) ? '{' ( pass[technique_decl] )+  '}' SEMI?
    {
      fxDesc_->Techniques.push_back(technique_decl);
    }

  | TECHNIQUE
    {
      technique_decl.Name = "";
    }
    ( annotation_list[technique_decl.Annotations] ) ? '{' ( pass[technique_decl] )+  '}' SEMI?
    {
      fxDesc_->Techniques.push_back(technique_decl);
    }
  ;

pass [TechniqueDesc& technique_decl]
@declarations {
  PassDesc pass_decl;
}
@after {
  technique_decl.Passes.push_back(pass_decl);
}
  : PASS IDENTIFIER?
    {
      if ($IDENTIFIER != NULL && $IDENTIFIER.text->chars != NULL) {
        pass_decl.Name =  toString($IDENTIFIER.text);
      } else {
        *error_string_ += "Bad pass identifier, line ";
        *error_string_ += $PASS.line;
      }
    }
    (annotation_list[pass_decl.Annotations] ) ? '{' ( state_assignment[pass_decl] )*  WHITESPACE* '}' SEMI?
  ;

state_assignment [PassDesc& pass]
  : (VERTEXSHADER) =>
      VERTEXSHADER '=' COMPILE_SHADER IDENTIFIER fn=variable_or_call_expression
      {
        pass.Shaders[ShaderType::VertexShader].EntryPoint = $fn.identifier;
        pass.Shaders[ShaderType::VertexShader].Profile = toString($IDENTIFIER.text);
      }
      SEMI
  | 'SetVertexShader' '(' 'CompileShader' '(' IDENTIFIER COMMA fn=variable_or_call_expression ')' ')'
      {
        pass.Shaders[ShaderType::VertexShader].EntryPoint = $fn.identifier;
        pass.Shaders[ShaderType::VertexShader].Profile = toString($IDENTIFIER.text);
      }
      SEMI
  | (FRAGMENTSHADER) =>
      x=FRAGMENTSHADER '=' COMPILE_SHADER IDENTIFIER fn=variable_or_call_expression
      {
        pass.Shaders[ShaderType::PixelShader].EntryPoint = $fn.identifier;
        pass.Shaders[ShaderType::PixelShader].Profile = toString($IDENTIFIER.text);
      }
    SEMI
  | 'SetPixelShader' '(' 'CompileShader' '(' IDENTIFIER COMMA fn=variable_or_call_expression ')' ')'
      {
        pass.Shaders[ShaderType::PixelShader].EntryPoint = $fn.identifier;
        pass.Shaders[ShaderType::PixelShader].Profile = toString($IDENTIFIER.text);
      }
      SEMI
  | IDENTIFIER '=' val=primary_expression
    SEMI
  ;

// data types ------------------------------------------------------------------

scalar_type_specifier
  : 'bool'
  | 'int'
  | 'uint'
  | 'half'
  | FLOAT
  | 'double'
  ;

scalar_type_or_string_specifier
  : scalar_type_specifier
  | string_type_specifier
  ;

vector_type_specifier
  : 'bool1'
  | 'bool2'
  | 'bool3'
  | 'bool4'
  | 'int1'
  | 'int2'
  | 'int3'
  | 'int4'
  | 'uint1'
  | 'uint2'
  | 'uint3'
  | 'uint4'
  | 'half1'
  | 'half2'
  | 'half3'
  | 'half4'
  | 'float1'
  | 'float2'
  | 'float3'
  | 'float4'
  | 'double1'
  | 'double2'
  | 'double3'
  | 'double4'
  | VECTOR '<' scalar_type_specifier ',' DECIMAL_LITERAL '>'
  ;

matrix_type_specifier
  : 'float1x1'
  | 'float1x2'
  | 'float1x3'
  | 'float1x4'
  | 'float2x1'
  | 'float2x2'
  | 'float2x3'
  | 'float2x4'
  | 'float3x1'
  | 'float3x2'
  | 'float3x3'
  | 'float3x4'
  | 'float4x1'
  | 'float4x2'
  | 'float4x3'
  | 'float4x4'
  | MATRIX '<' scalar_type_specifier ','
    DECIMAL_LITERAL ',' DECIMAL_LITERAL '>'
  ;

COMPILE_SHADER
  : 'compile'
  | 'CompileShader'
  ;

string_type_specifier
  : STRING
  ;

// Sampler declarations ----------------------------
sampler_type_specifier
  : SAMPLER
  | SAMPLER1D
  | SAMPLER2D
  | SAMPLER3D
  | SAMPLERCUBE
  | SAMPLERSTATE
  | 'SamplerComparisonState' | 'samplercomparisonstate'  // DX10 only
  ;

 // texture declaration ----------------------------

texture_type_specifier
  : TEXTURE
  | TEXTURE1D
  | TEXTURE2D
  | TEXTURE3D
  | TEXTURECUBE
  | TEXTURERECT
  ;

 // struct declaration -----------------------------

struct_type_specifier
  : IDENTIFIER
  | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition
  | STRUCT IDENTIFIER
  ;

annotation_list[std::vector<AnnotationDesc>& annotations]
@declarations {
  AnnotationDesc annotationDesc;
}
  : '<' ( annotation[annotations] ) * '>'
  ;

annotation[std::vector<AnnotationDesc>& annotations]
@declarations {
  AnnotationDesc annotationDesc;
}
  : basic_type_specifier IDENTIFIER '=' initializer SEMI
  {
  	annotationDesc.Type = toString($basic_type_specifier.text);
 	annotationDesc.Name = toString($IDENTIFIER.text);
 	annotationDesc.Value = $initializer.valueInit;
 	annotations.push_back(annotationDesc);
  }
  ;

initializer returns [std::string valueInit]
  :  string_literal {$valueInit = toString($string_literal.text); $valueInit = $valueInit.substr(1, $valueInit.size()-2); }
  | expression {$valueInit = toString($expression.text); }
  | '{' expression ( ',' expression )* '}' {$valueInit = toString($initializer.text); }
  ;

register_name
  // registers for VS_3_0
  :    REGISTER '(' input_register_name | output_register_name ')';

input_register_name
  : IDENTIFIER DECIMAL_LITERAL
    // IDENTIFIER must be v, r, c, b, i, s, o (check semantically)
  | IDENTIFIER
    // IDENTIFIER must be a0 aL p0 (check semantically)
  ;

output_register_name
  : IDENTIFIER
  // must be one of:  'oD0', 'oD1', 'oFog', 'oPos', 'oPts',
  //                  'oT0', 'oT1', 'oT2', 'oT3', 'oT4', 'oT5', 'oT6', 'oT7'
  // (check semantically)
  ;

pack_offset
  : .+ ;   // no idea what this field looks like.

argument_list
  : ( param_declaration ( COMMA param_declaration )* )?
  ;

param_declaration
  : param_direction? param_variability? param_type_specifier id_declaration semantic?
//    | FUNCTION type_specifier IDENTIFIER
  ;

param_variability
  : T_CONST
  | UNIFORM
  ;

param_direction
  : T_IN
  | T_OUT
  | T_INOUT
  ;

function_body
  : LCURLY ( function_instructions )* RCURLY
  ;
  
function_instructions
  // We copied the following sub-rule here to expedite the parsing time
  // as this is a much more common case than the "Id init_declarator_list"
  // case which would normally spend a lot of time in exception handling.
  : noise
  | instruction
  | LCURLY function_instructions RCURLY
  ;

instruction
	: INSTRUCTION_GENERIC;

declaration_statement
  : ( T_CONST )? var_datatype init_declarator_list SEMI
  ;

init_declarator_list
  : init_declarator ( COMMA init_declarator )* ;

init_declarator
  : declarator ( ASSIGN expression )? ;

declarator
  : IDENTIFIER ( LBRACKET ( constant_expression )? RBRACKET )*
  ;

struct_definition
  : STRUCT IDENTIFIER LCURLY struct_declaration_list RCURLY IDENTIFIER? SEMI
  ;

struct_declaration_list
    // We currently don't support nested structs so the field type
    // can only be either a scalar or a vector.
  : ( struct_interpolation_modifier?
    (scalar_type_specifier|vector_type_specifier) id_declaration
    (COLON semantic_specifier)? SEMI )+
  ;

struct_interpolation_modifier   // DX10 only
  : T_LINEAR
  | CENTROID
  | NOINTERPOLATION
  | NOPERSPECTIVE
  ;

semantic_specifier
  : IDENTIFIER
  ;

statement
  : ( lvalue_expression assignment_operator ) => assignment_statement
  | ( lvalue_expression self_modify_operator ) => post_modify_statement
  | pre_modify_statement
  | expression_statement
  | compound_statement
  | selection_statement
  | iteration_statement
  | jump_statement
  | SEMI
  ;

assignment_statement
  : lvalue_expression assignment_operator expression SEMI
  ;

pre_modify_statement
  : pre_modify_expression SEMI ;

pre_modify_expression
  : self_modify_operator lvalue_expression ;

post_modify_statement
  : post_modify_expression SEMI ;

post_modify_expression
  : lvalue_expression self_modify_operator ;

self_modify_operator
  : PLUS_PLUS | MINUS_MINUS ;

expression_statement
  : expression SEMI ;

compound_statement
  : LCURLY (
          ( IDENTIFIER init_declarator_list) => IDENTIFIER init_declarator_list SEMI
        | ( ( T_CONST )? vector_type_specifier ) => ( T_CONST )? vector_type_specifier init_declarator_list SEMI
        | ( ( T_CONST )? scalar_type_specifier ) => ( T_CONST )? scalar_type_specifier init_declarator_list SEMI
        | ( STRUCT ( IDENTIFIER )? LCURLY ) => struct_definition ( init_declarator_list )? SEMI
        | STRUCT IDENTIFIER init_declarator_list SEMI
        | statement
        )*
    RCURLY
  ;

selection_statement
  : IF LPAREN expression RPAREN statement ( ELSE statement )?
  ;

iteration_statement
  : WHILE LPAREN expression RPAREN statement
  | FOR LPAREN assignment_statement
      equality_expression SEMI modify_expression RPAREN statement
  | DO statement WHILE LPAREN expression RPAREN SEMI
  ;

modify_expression
  : (lvalue_expression assignment_operator ) =>
      lvalue_expression assignment_operator expression
  | pre_modify_expression
  | post_modify_expression
  ;

jump_statement
  : BREAK SEMI
  | CONTINUE
  | RETURN ( expression )? SEMI
  | DISCARD
  ;

expression
  : conditional_expression
  ;

assignment_operator
  : ASSIGN
  | MUL_ASSIGN
  | DIV_ASSIGN
  | ADD_ASSIGN
  | SUB_ASSIGN
  | BITWISE_AND_ASSIGN
  | BITWISE_OR_ASSIGN
  | BITWISE_XOR_ASSIGN
  | BITWISE_SHIFTL_ASSIGN
  | BITWISE_SHIFTR_ASSIGN
  ;

constant_expression
  : (IDENTIFIER) => variable_expression
  | literal_value
  ;

conditional_expression
  : logical_or_expression ( QUESTION expression COLON conditional_expression )?
  ;

logical_or_expression
  : exclusive_or_expression ( OR exclusive_or_expression )*
  ;

// We remove the NOT operator from the unary expression and stick it here
// so that it has a lower precedence than relational operations.
logical_and_expression
  : ( NOT )? inclusive_or_expression ( AND ( NOT )? inclusive_or_expression )*
  ;

inclusive_or_expression
  : exclusive_or_expression (BITWISE_OR exclusive_or_expression )*
  ;

exclusive_or_expression
  : and_expression ( BITWISE_XOR and_expression )*
  ;

and_expression
  : equality_expression ( BITWISE_AND equality_expression )*
  ;

equality_expression
  : relational_expression ( (EQUAL|NOT_EQUAL) relational_expression )*
  ;

relational_expression
  : shift_expression ( (LESS|GREATER|LESSEQUAL|GREATEREQUAL) shift_expression )*
  ;

shift_expression
  : additive_expression ( (BITWISE_SHIFTL|BITWISE_SHIFTR) additive_expression )*
  ;

additive_expression
  : multiplicative_expression ( (PLUS|MINUS) multiplicative_expression )*
  ;

multiplicative_expression
  : cast_expression ( (MUL|DIV|MOD) cast_expression )*
  ;

cast_expression
  : LPAREN IDENTIFIER RPAREN postfix_expression
  | LPAREN basic_type_specifier RPAREN postfix_expression
  | unary_expression
  ;

unary_expression
  : (PLUS|MINUS) unary_expression
  | postfix_expression
  ;

postfix_expression
  : primary_expression ( postfix_suffix )?
  ;

lvalue_expression
  : variable_expression ( postfix_suffix )?
  ;

postfix_suffix
  // choosing between struct field access or vector swizzling is a semantic choice.
  : ( DOT primary_expression )+
  ;

primary_expression
  : constructor
  | literal_value
  | variable_or_call_expression
  | LPAREN expression RPAREN
  ;

variable_expression
  : IDENTIFIER ( LBRACKET expression RBRACKET )?
  ;

// Combine variable expression and call expression here to get rid of the
// syntactic predicate we used to use in the primary_expression rule. Using
// predicates results in the parser spending a lot of time in exception
// handling (when lookahead fails).
variable_or_call_expression
  returns [std::string identifier, std::string arglist]
  : IDENTIFIER
    (
      ( ( LBRACKET expression RBRACKET )? )
      {
        $identifier = toString($IDENTIFIER.text);
        $arglist = "";
      }
      |
      ( LPAREN argument_expression_list RPAREN )
      {
        $identifier = toString($IDENTIFIER.text);
        if ($argument_expression_list.text->chars) {
          $arglist = toString($argument_expression_list.text);
        } else {
          $arglist = "";
        }
      }
    )
  ;

constructor
  : ( vector_type_specifier | matrix_type_specifier )
    LPAREN expression ( COMMA expression )* RPAREN
  ;

argument_expression_list
  : ( expression ( COMMA expression )* )?
  ;

int_literal
  : DECIMAL_LITERAL
//  | OCT_LITERAL
//  | HEX_LITERAL
  ;

literal_value
  : DECIMAL_LITERAL
  | float_literal
  | string_literal
  | ( T_FALSE | T_TRUE )
  ;

float_literal
  : FLOAT_LITERAL
  ;

string_literal returns [std::string content]
  : STRING_LITERAL
  ;
  
// lexical elements ------------------------------------------------------------

NOT           : '!' ;
NOT_EQUAL     : '!=' ;
AND           : '&&' ;
LPAREN        : '(' ;
RPAREN        : ')' ;
MUL           : '*' ;
MUL_ASSIGN    : '*=' ;
PLUS          : '+' ;
PLUS_PLUS     : '++' ;
ADD_ASSIGN    : '+=' ;
COMMA         : ',' ;
MINUS         : '-' ;
MINUS_MINUS   : '--' ;
SUB_ASSIGN    : '-=' ;
DIV           : '/' ;
DIV_ASSIGN    : '/=' ;
MOD           : '%';
MOD_ASSIGN    :  '%=';
COLON         : ':' ;
SEMI          : ';' ;
LESS          : '<' ;
LESSEQUAL     : '<=' ;
ASSIGN        : '=' ;
EQUAL         : '==' ;
GREATER       : '>' ;
GREATEREQUAL  : '>=' ;
QUESTION      : '?' ;
LBRACKET      : '[' ;
RBRACKET      : ']' ;
LCURLY        : '{' ;
OR            : '||' ;
RCURLY        : '}' ;
DOT           : '.' ;
BITWISE_NOT    : '~';
BITWISE_SHIFTL : '<<';
BITWISE_SHIFTR : '>>';
BITWISE_AND    : '&';
BITWISE_OR     : '|';
BITWISE_XOR    : '^';
BITWISE_SHIFTL_ASSIGN : '<<=';
BITWISE_SHIFTR_ASSIGN : '>>=';
BITWISE_AND_ASSIGN    : '&=';
BITWISE_OR_ASSIGN     : '|=';
BITWISE_XOR_ASSIGN    : '^=';
// keywords ----------------------------

BREAK            : 'break';
BUFFER           : 'buffer';
COLUMN_MAJOR     : 'column_major';
CBUFFER          : 'cbuffer';
CENTROID         : 'centroid';
T_CONST          : 'const';
CONTINUE         : 'continue';
DISCARD          : 'discard';
DO               : 'do';
ELSE             : 'else';
EXTERN           : 'extern';
T_FALSE          : 'false';
FLOAT            : (('f'|'F')('l'|'L')('o'|'O')('a'|'A')('t'|'T'));
FOR              : 'for';
IF               : 'if';
T_IN             : 'in';
INLINE           : 'inline';
T_INOUT          : 'inout';
T_LINEAR         : 'linear';
MATRIX           : ('m'|'M')('a'|'A')('t'|'T')('r'|'R')('i'|'I')('x'|'X');
NAMESPACE        : 'namespace';
NOINTERPOLATION  : 'nointerpolation';
NOPERSPECTIVE    : 'noperspective';
T_OUT            : 'out';
RETURN           : 'return';
REGISTER         : 'register';
ROW_MAJOR        : 'row_major';
SAMPLER		 : ('s'|'S')'ampler';
SAMPLER1D		 : ('s'|'S')'ampler1D';
SAMPLER2D		 : ('s'|'S')'ampler2D';
SAMPLER3D		 : ('s'|'S')'ampler3D';
SAMPLERCUBE		 : ('s'|'S')'amplerCUBE';
SAMPLERSTATE		 : ('s'|'S')'ampler_state';
SHARED           : 'shared';
STATEBLOCK       : 'stateblock';
STATEBLOCK_STATE : 'stateblock_state';
STATIC           : 'static';
STRING           : ('s'|'S')('t'|'T')('r'|'R')('i'|'I')('n'|'N')('g'|'G');
STRUCT           : 'struct';
SWITCH           : 'switch';
TBUFFER          : 'tbuffer';
TEXTURE          :
  ('t'|'T')('e'|'E')('x'|'X')('t'|'T')('u'|'U')('r'|'R')('e'|'E');
TEXTURE1D        : 'Texture1D';
TEXTURE1DARRAY   : 'Texture1DArray';
TEXTURE2D        : 'Texture2D';
TEXTURE2DARRAY   : 'Texture2DArray';
TEXTURE2DMS      : 'Texture2DMS';
TEXTURE2DMSARRAY : 'Texture2DMSArray';
TEXTURE3D        : 'Texture3D';
TEXTURECUBE      : 'TextureCUBE';
TEXTURECUBEARRAY : 'TextureCUBEArray';
TEXTURERECT      : 'TextureRECT';
T_TRUE           : 'true';
TYPEDEF          : 'typedef';
UNIFORM          : 'uniform';
VECTOR           : ('v'|'V')('e'|'E')('c'|'C')('t'|'T')('o'|'O')('r'|'R');
T_VOID           : 'void';
VOLATILE         : 'volatile';
WHILE            : 'while';

// sampler state tokens

PASS           : ('p'|'P')('a'|'A')('s'|'S')('s'|'S');

TECHNIQUE      : ('t'|'T')('e'|'E')('c'|'C')('h'|'H')
                 ('n'|'N')('i'|'I')('q'|'Q')('u'|'U')('e'|'E')('10'|'11')?
               ;

VERTEXSHADER
  : (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X'))
    ((('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R')) |
     (('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M')))
  ;

FRAGMENTSHADER
  : (('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T')
     ('p'|'P')('r'|'R')('o'|'O')('g'|'G')('r'|'R')('a'|'A')('m'|'M'))
  | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L')
     ('s'|'S')('h'|'H')('a'|'A')('d'|'D')('e'|'E')('r'|'R'))
  ;

RESERVED_WORDS
  : (('a'|'A')('S'|'s')('m'|'M'))
  | 'asm_fragment'
  | 'auto'
  | 'case'
  | 'catch'
  | 'char'
  | 'class'
  | 'const_cast'
  | (('d'|'D')('e'|'E')('c'|'C')('l'|'L'))
  | 'default'
  | 'delete'
  | (('d'|'D')('w'|'W')('o'|'O')('r'|'R')('d'|'D'))
  | 'dynamic_cast'
  | 'emit'
  | 'enum'
  | 'explicit'
  | 'fixed'
  | 'friend'
  | 'get'
  | 'goto'
  | 'interface'
  | 'long'
  | 'mutable'
  | 'new'
  | 'operator'
  | 'packed'
  | (('p'|'P')('i'|'I')('x'|'X')('e'|'E')('l'|'L')
     ('f'|'F')('r'|'R')('a'|'A')('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T'))
  | 'private'
  | 'protected'
  | 'public'
  | 'reinterpret_cast'
  | 'short'
  | 'signed'
  | 'sizeof'
  | 'snorm'
  | 'static_cast'
  | 'template'
  | 'this'
  | 'throw'
  | 'try'
  | 'typeid'
  | 'typename'
  | 'union'
  | 'unorm'
  | 'unsigned'
  | 'using'
  | (('v'|'V')('e'|'E')('r'|'R')('t'|'T')('e'|'E')('x'|'X')
     ('f'|'F')('r'|'R')('a'|'A')
     ('g'|'G')('m'|'M')('e'|'E')('n'|'N')('t'|'T'))
  | 'virtual'
  ;

IDENTIFIER
  : ('a'..'z'|'A'..'Z'|'_')('a'..'z'|'A'..'Z'|'_'|'0'..'9')*
  ;

DECIMAL_LITERAL
  : ('0'..'9')+
  ;


fragment ESCAPE_SEQUENCE
  : '\\' ('b'|'t'|'n'|'f'|'r'|'\"'|'\''|'\\')
  ;

CHARACTER_LITERAL
  :   '\'' ( ESCAPE_SEQUENCE | ~('\''|'\\') ) '\''
  ;

STRING_LITERAL
  :  '"' (  ~('\\'|'"') | ESCAPE_SEQUENCE )* '"'
  ;

fragment EXPONENT : ('e'|'E') (PLUS | MINUS)? ('0'..'9')+ ;

fragment FLOATSUFFIX : ('f'|'F'|'h'|'H') ;

FLOAT_LITERAL
  : ('0'..'9')+ '.' ('0'..'9')* (EXPONENT)? (FLOATSUFFIX)?
  | '.' ('0'..'9')+ (EXPONENT)? (FLOATSUFFIX)?
  ;

// skipped elements ------------------------------------------------------------

LINE_DIRECTIVE
  : '#line' (' '|'\t')* line_num=DECIMAL_LITERAL '\r'? '\n'
    { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN; }
  | '#line' (' '|'\t')* line_num=DECIMAL_LITERAL
    (' '|'\t')* file=STRING_LITERAL '\r'? '\n'
    { INPUT->setLine(INPUT, toInt($line_num.text)); $channel = HIDDEN;
      setFilename(RECOGNIZER, $file.text); }
  ;

WHITESPACE
  : (' '|'\r'|'\t'|'\u000C'|'\n')   { $channel = HIDDEN; }
  ;

COMMENT
  : '//' ~('\n'|'\r')* '\r'? '\n'   { $channel = HIDDEN; }
  ;

MULTILINE_COMMENT
  : '/*' ( options {greedy=false;} : . )* '*/'  { $channel = HIDDEN; }
  ;

INSTRUCTION_GENERIC
  : ( options {greedy=false;} : . )* ';'
	;

// -----------------------------------------------------------------------------
